/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2001-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sun.org.apache.xerces.internal.impl.xs;

import com.sun.org.apache.xerces.internal.impl.dv.ValidatedInfo;
import com.sun.org.apache.xerces.internal.impl.xs.util.XSObjectListImpl;
import com.sun.org.apache.xerces.internal.xs.XSAnnotation;
import com.sun.org.apache.xerces.internal.xs.XSAttributeGroupDefinition;
import com.sun.org.apache.xerces.internal.xs.XSAttributeUse;
import com.sun.org.apache.xerces.internal.xs.XSConstants;
import com.sun.org.apache.xerces.internal.xs.XSNamespaceItem;
import com.sun.org.apache.xerces.internal.xs.XSObjectList;
import com.sun.org.apache.xerces.internal.xs.XSWildcard;

/**
 * The XML representation for an attribute group declaration
 * schema component is a global <attributeGroup> element information item
 *
 * @author Sandy Gao, IBM
 * @author Rahul Srivastava, Sun Microsystems Inc.
 * @version $Id: XSAttributeGroupDecl.java,v 1.7 2010-11-01 04:39:55 joehw Exp $
 * @xerces.internal
 */
public class XSAttributeGroupDecl implements XSAttributeGroupDefinition {

  // name of the attribute group
  public String fName = null;
  // target namespace of the attribute group
  public String fTargetNamespace = null;
  // number of attribute uses included by this attribute group
  int fAttrUseNum = 0;
  // attribute uses included by this attribute group
  private static final int INITIAL_SIZE = 5;
  XSAttributeUseImpl[] fAttributeUses = new XSAttributeUseImpl[INITIAL_SIZE];
  // attribute wildcard included by this attribute group
  public XSWildcardDecl fAttributeWC = null;
  // whether there is an attribute use whose type is or is derived from ID.
  public String fIDAttrName = null;

  // optional annotation
  public XSObjectList fAnnotations;

  protected XSObjectListImpl fAttrUses = null;

  // The namespace schema information item corresponding to the target namespace
  // of the attribute group definition, if it is globally declared; or null otherwise.
  private XSNamespaceItem fNamespaceItem = null;

  // add an attribute use
  // if the type is derived from ID, but there is already another attribute
  // use of type ID, then return the name of the other attribute use;
  // otherwise, return null
  public String addAttributeUse(XSAttributeUseImpl attrUse) {

    // if this attribute use is prohibited, then don't check whether it's
    // of type ID
    if (attrUse.fUse != SchemaSymbols.USE_PROHIBITED) {
      if (attrUse.fAttrDecl.fType.isIDType()) {
        // if there is already an attribute use of type ID,
        // return its name (and don't add it to the list, to avoid
        // interruption to instance validation.
        if (fIDAttrName == null) {
          fIDAttrName = attrUse.fAttrDecl.fName;
        } else {
          return fIDAttrName;
        }
      }
    }

    if (fAttrUseNum == fAttributeUses.length) {
      fAttributeUses = resize(fAttributeUses, fAttrUseNum * 2);
    }
    fAttributeUses[fAttrUseNum++] = attrUse;

    return null;
  }

  public void replaceAttributeUse(XSAttributeUse oldUse, XSAttributeUseImpl newUse) {
    for (int i = 0; i < fAttrUseNum; i++) {
      if (fAttributeUses[i] == oldUse) {
        fAttributeUses[i] = newUse;
      }
    }
  }

  public XSAttributeUse getAttributeUse(String namespace, String name) {
    for (int i = 0; i < fAttrUseNum; i++) {
      if ((fAttributeUses[i].fAttrDecl.fTargetNamespace == namespace) &&
          (fAttributeUses[i].fAttrDecl.fName == name)) {
        return fAttributeUses[i];
      }
    }

    return null;
  }

  public XSAttributeUse getAttributeUseNoProhibited(String namespace, String name) {
    for (int i = 0; i < fAttrUseNum; i++) {
      if ((fAttributeUses[i].fAttrDecl.fTargetNamespace == namespace) &&
          (fAttributeUses[i].fAttrDecl.fName == name) &&
          (fAttributeUses[i].fUse != SchemaSymbols.USE_PROHIBITED)) {
        return fAttributeUses[i];
      }
    }

    return null;
  }

  public void removeProhibitedAttrs() {
    if (fAttrUseNum == 0) {
      return;
    }
    // Remove all prohibited attributes.
    int count = 0;
    XSAttributeUseImpl[] uses = new XSAttributeUseImpl[fAttrUseNum];
    for (int i = 0; i < fAttrUseNum; i++) {
      if (fAttributeUses[i].fUse != SchemaSymbols.USE_PROHIBITED) {
        uses[count++] = fAttributeUses[i];
      }
    }
    fAttributeUses = uses;
    fAttrUseNum = count;

    // Do not remove attributes that have the same name as the prohibited
    // ones, because they are specified at the same level. Prohibited
    // attributes are only to remove attributes from the base type in a
    // restriction.
//        int newCount = 0;
//        if (pCount > 0) {
//            OUTER: for (int i = 0; i < fAttrUseNum; i++) {
//                if (fAttributeUses[i].fUse == SchemaSymbols.USE_PROHIBITED)
//                    continue;
//                for (int j = 1; j <= pCount; j++) {
//                    if (fAttributeUses[i].fAttrDecl.fName == pUses[fAttrUseNum-pCount].fAttrDecl.fName &&
//                        fAttributeUses[i].fAttrDecl.fTargetNamespace == pUses[fAttrUseNum-pCount].fAttrDecl.fTargetNamespace) {
//                        continue OUTER;
//                    }
//                }
//                pUses[newCount++] = fAttributeUses[i];
//            }
//            fAttributeUses = pUses;
//            fAttrUseNum = newCount;
//        }
  }

  /**
   * Check that the attributes in this group validly restrict those from a base group. If an error
   * is found, an Object[] is returned. This contains the arguments for the error message describing
   * the error. The last element in the array (at index arr.length - 1) is the the error code.
   * Returns null if there is no error.
   *
   * REVISIT: is there a better way of returning the appropriate information for the error?
   *
   * @param typeName the name of the type containing this attribute group, used for error reporting
   * purposes
   * @param baseGroup the XSAttributeGroupDecl that is the base we are checking against
   */
  public Object[] validRestrictionOf(String typeName, XSAttributeGroupDecl baseGroup) {

    Object[] errorArgs = null;
    XSAttributeUseImpl attrUse = null;
    XSAttributeDecl attrDecl = null;
    XSAttributeUseImpl baseAttrUse = null;
    XSAttributeDecl baseAttrDecl = null;

    for (int i = 0; i < fAttrUseNum; i++) {

      attrUse = fAttributeUses[i];
      attrDecl = attrUse.fAttrDecl;

      // Look for a match in the base
      baseAttrUse = (XSAttributeUseImpl) baseGroup
          .getAttributeUse(attrDecl.fTargetNamespace, attrDecl.fName);
      if (baseAttrUse != null) {
        //
        // derivation-ok-restriction.  Constraint 2.1.1
        //

        if (baseAttrUse.getRequired() && !attrUse.getRequired()) {
          errorArgs = new Object[]{typeName, attrDecl.fName,
              attrUse.fUse == SchemaSymbols.USE_OPTIONAL ? SchemaSymbols.ATTVAL_OPTIONAL
                  : SchemaSymbols.ATTVAL_PROHIBITED,
              "derivation-ok-restriction.2.1.1"};
          return errorArgs;
        }

        // if this attribute is prohibited in the derived type, don't
        // need to check any of the following constraints.
        if (attrUse.fUse == SchemaSymbols.USE_PROHIBITED) {
          continue;
        }

        baseAttrDecl = baseAttrUse.fAttrDecl;
        //
        // derivation-ok-restriction.  Constraint 2.1.1
        //
        if (!XSConstraints.checkSimpleDerivationOk(attrDecl.fType,
            baseAttrDecl.fType,
            baseAttrDecl.fType.getFinal())) {
          errorArgs = new Object[]{typeName, attrDecl.fName, attrDecl.fType.getName(),
              baseAttrDecl.fType.getName(), "derivation-ok-restriction.2.1.2"};
          return errorArgs;
        }

        //
        // derivation-ok-restriction.  Constraint 2.1.3
        //
        int baseConsType = baseAttrUse.fConstraintType != XSConstants.VC_NONE ?
            baseAttrUse.fConstraintType : baseAttrDecl.getConstraintType();
        int thisConstType = attrUse.fConstraintType != XSConstants.VC_NONE ?
            attrUse.fConstraintType : attrDecl.getConstraintType();

        if (baseConsType == XSConstants.VC_FIXED) {

          if (thisConstType != XSConstants.VC_FIXED) {
            errorArgs = new Object[]{typeName, attrDecl.fName,
                "derivation-ok-restriction.2.1.3.a"};
            return errorArgs;
          } else {
            // check the values are the same.
            ValidatedInfo baseFixedValue = (baseAttrUse.fDefault != null ?
                baseAttrUse.fDefault : baseAttrDecl.fDefault);
            ValidatedInfo thisFixedValue = (attrUse.fDefault != null ?
                attrUse.fDefault : attrDecl.fDefault);
            if (!baseFixedValue.actualValue.equals(thisFixedValue.actualValue)) {
              errorArgs = new Object[]{typeName, attrDecl.fName, thisFixedValue.stringValue(),
                  baseFixedValue.stringValue(), "derivation-ok-restriction.2.1.3.b"};
              return errorArgs;
            }

          }

        }
      } else {
        // No matching attribute in base - there should be a matching wildcard

        //
        // derivation-ok-restriction.  Constraint 2.2
        //
        if (baseGroup.fAttributeWC == null) {
          errorArgs = new Object[]{typeName, attrDecl.fName,
              "derivation-ok-restriction.2.2.a"};
          return errorArgs;
        } else if (!baseGroup.fAttributeWC.allowNamespace(attrDecl.fTargetNamespace)) {
          errorArgs = new Object[]{typeName, attrDecl.fName,
              attrDecl.fTargetNamespace == null ? "" : attrDecl.fTargetNamespace,
              "derivation-ok-restriction.2.2.b"};
          return errorArgs;
        }
      }
    }

    //
    // Check that any REQUIRED attributes in the base have matching attributes
    // in this group
    // derivation-ok-restriction.  Constraint 3
    //
    for (int i = 0; i < baseGroup.fAttrUseNum; i++) {

      baseAttrUse = baseGroup.fAttributeUses[i];

      if (baseAttrUse.fUse == SchemaSymbols.USE_REQUIRED) {

        baseAttrDecl = baseAttrUse.fAttrDecl;
        // Look for a match in this group
        if (getAttributeUse(baseAttrDecl.fTargetNamespace, baseAttrDecl.fName) == null) {
          errorArgs = new Object[]{typeName, baseAttrUse.fAttrDecl.fName,
              "derivation-ok-restriction.3"};
          return errorArgs;
        }
      }
    }

    // Now, check wildcards
    //
    // derivation-ok-restriction.  Constraint 4
    //
    if (fAttributeWC != null) {
      if (baseGroup.fAttributeWC == null) {
        errorArgs = new Object[]{typeName, "derivation-ok-restriction.4.1"};
        return errorArgs;
      }
      if (!fAttributeWC.isSubsetOf(baseGroup.fAttributeWC)) {
        errorArgs = new Object[]{typeName, "derivation-ok-restriction.4.2"};
        return errorArgs;
      }
      if (fAttributeWC.weakerProcessContents(baseGroup.fAttributeWC)) {
        errorArgs = new Object[]{typeName,
            fAttributeWC.getProcessContentsAsString(),
            baseGroup.fAttributeWC.getProcessContentsAsString(),
            "derivation-ok-restriction.4.3"};
        return errorArgs;
      }
    }

    return null;

  }

  static final XSAttributeUseImpl[] resize(XSAttributeUseImpl[] oldArray, int newSize) {
    XSAttributeUseImpl[] newArray = new XSAttributeUseImpl[newSize];
    System.arraycopy(oldArray, 0, newArray, 0, Math.min(oldArray.length, newSize));
    return newArray;
  }

  // reset the attribute group declaration
  public void reset() {
    fName = null;
    fTargetNamespace = null;
    // reset attribute uses
    for (int i = 0; i < fAttrUseNum; i++) {
      fAttributeUses[i] = null;
    }
    fAttrUseNum = 0;
    fAttributeWC = null;
    fAnnotations = null;
    fIDAttrName = null;

  }

  /**
   * Get the type of the object, i.e ELEMENT_DECLARATION.
   */
  public short getType() {
    return XSConstants.ATTRIBUTE_GROUP;
  }

  /**
   * The <code>name</code> of this <code>XSObject</code> depending on the
   * <code>XSObject</code> type.
   */
  public String getName() {
    return fName;
  }

  /**
   * The namespace URI of this node, or <code>null</code> if it is
   * unspecified.  defines how a namespace URI is attached to schema
   * components.
   */
  public String getNamespace() {
    return fTargetNamespace;
  }

  /**
   * {attribute uses} A set of attribute uses.
   */
  public XSObjectList getAttributeUses() {
    if (fAttrUses == null) {
      fAttrUses = new XSObjectListImpl(fAttributeUses, fAttrUseNum);
    }
    return fAttrUses;
  }

  /**
   * {attribute wildcard} Optional. A wildcard.
   */
  public XSWildcard getAttributeWildcard() {
    return fAttributeWC;
  }

  /**
   * Optional. Annotation.
   */
  public XSAnnotation getAnnotation() {
    return (fAnnotations != null) ? (XSAnnotation) fAnnotations.item(0) : null;
  }

  /**
   * Optional. Annotations.
   */
  public XSObjectList getAnnotations() {
    return (fAnnotations != null) ? fAnnotations : XSObjectListImpl.EMPTY_LIST;
  }

  /**
   * @see org.apache.xerces.xs.XSObject#getNamespaceItem()
   */
  public XSNamespaceItem getNamespaceItem() {
    return fNamespaceItem;
  }

  void setNamespaceItem(XSNamespaceItem namespaceItem) {
    fNamespaceItem = namespaceItem;
  }

} // class XSAttributeGroupDecl
